<script setup lang="ts">
import {ref} from "vue";
import {ElButton, ElMessage} from "element-plus";
import {devDownloadProps} from "./download";
import {devGetFileNameFromUrl, useDevMethods} from "@/utils";

const props = defineProps(devDownloadProps);
const methods = useDevMethods();
const loading = ref(false);
const emit = defineEmits(['success','error','complete']);

const isHttp = (url: string) => {
  return /^https?:\/\//.test(url);
};

// 创建和点击下载链接
const createDownloadLink = (href: string, downloadName: string='') => {
  const link = document.createElement('a');
  link.href = href;
  // 如果用户自定义了下载名称那么就使用用户自定义的名称
  if (props.name || props.type === 'image') {
    link.setAttribute('download', downloadName);
  }
  document.body.appendChild(link);
  link.click();
  document.body.removeChild(link);
  loading.value = false;
};

const downloadImage = async (imgSrc: string, str: string='') => {
  const name_ = str?str:devGetFileNameFromUrl(imgSrc,true);
  if (!imgSrc || !isHttp(imgSrc)) {
    ElMessage.error('图片地址错误');
    emit('error', '图片地址错误');
    emit('complete')
    loading.value = false;
    return;
  }

  const image = new Image();
  image.crossOrigin = 'anonymous';
  image.src = imgSrc;

  image.onload = () => {
    const canvas = document.createElement("canvas");
    canvas.width = image.width;
    canvas.height = image.height;
    const context = canvas.getContext("2d");
    if (context) {
      context.drawImage(image, 0, 0, image.width, image.height);
      const url = canvas.toDataURL(props.imageType);
      createDownloadLink(url,name_);
      emit('success', imgSrc);
      emit('complete')
    }
  };

  image.onerror = () => {
    if(props.allowFallback){
      console.warn('图片加载失败，可能是 Content-Disposition 限制，尝试直接下载');
      createDownloadLink(imgSrc,name_);
      return;
    }
    emit('error','图片加载失败');
    emit('complete')
    loading.value = false;
  };
};

/**
 * 下载
 */
const clickDownload = () => {
  if (!props.disabled && !loading.value) {
    loading.value = true; // 开始下载前设置 loading
    props.useApi ? asyncDownload() : normalDownload();
  }
};

// 普通下载
const normalDownload = () => {
  if (props.type === 'file') {
    if (!props.url || !isHttp(props.url)) {
      ElMessage.error('文件地址错误');
      emit('error','文件地址错误');
      emit('complete')
      loading.value = false;
      return;
    }
    createDownloadLink(props.url);
    emit('success', props.url);
    emit('complete')
  } else if (props.type === 'image') {
    downloadImage(props.url, props.name);
  }else {
    ElMessage.error(`普通下载模式下仅支持"file"和"image",当前类型:${props.type}`);
    emit('error', '不支持的下载类型');
    emit('complete')
    loading.value = false;
  }
};

// 异步下载
const asyncDownload = async () => {
  loading.value = true;
  try {
    const res_ = await props.api(props.apiParams);
    const res = props.devDownloadFieldsFn ? props.devDownloadFieldsFn(res_) : methods.devDownloadFieldsFn(res_)
    if (props.type === 'file') {
      if (!res || !isHttp(res)) {
        ElMessage.error('文件地址错误');
        emit('error', '文件地址错误');
        loading.value = false;
      } else {
        createDownloadLink(res);
        emit('success', res);
      }
    } else if (props.type === 'image') {
      downloadImage(res, props.name);
    } else if (props.type === 'blob') {
      const blob = new Blob([res], props.blobOptions);
      const url = URL.createObjectURL(blob);
      const downloadName = props.name?`${props.name}${props.fileType}`:devGetFileNameFromUrl(url,true);
      createDownloadLink(url, downloadName);
      emit('success', res);
      URL.revokeObjectURL(url); // 释放 Blob URL
    } else {
      ElMessage.error(`不支持的下载类型:${props.type}`);
      emit('error', '不支持的下载类型');
    }
  } catch {
    ElMessage.error('异步下载失败');
    emit('error', '异步下载失败');
  } finally {
    loading.value = false;
    emit('complete')
  }
};

// 暴露出去的方法
defineExpose({
  clickDownload
});

// 定义组件名称
defineOptions({
  name: 'DevDownload'
});
</script>

<template>
  <slot v-if="!props.hideButton">
    <el-button @click.stop="clickDownload" :disabled="props.disabled" :type="props.buttonType" :link="props.buttonLink" :loading="loading">
      <slot name="buttonContent">{{ props.buttonContent }}</slot>
    </el-button>
  </slot>
</template>

<style scoped lang="scss"></style>
